#include <linux/fs.h> 
#include <linux/init.h> 
#include <linux/module.h> 
#include <asm/uaccess.h> 
#include <linux/cdev.h>
#include <linux/device.h> 

MODULE_LICENSE("GPL");
MODULE_VERSION( "2.0" ); 

#define DEVICE_FIRST  0 
#define DEVICE_COUNT  3 
#define MODNAME "scull" 
#define EOK 0 
#define DEVNAME "scull"

static char *hello_str = "Hello, world!\n"; 
static int scull_major = 0; 
static struct cdev modul;
static int device_open = 0;
static struct class *scull_class;  

static ssize_t scull_open(struct inode *n, struct file *f ) { 
   if( device_open ) return -EBUSY; 
   device_open++; 
   return EOK; 
}  

static ssize_t scull_read(struct file * file, char * buf, size_t count, loff_t *ppos ) { 
   	int len = strlen( hello_str ); 
  	 printk( KERN_INFO "=== read : %d\n", count ); 
   
	if( count < len ) return -EINVAL; 
   	if( *ppos != 0 ) { 
     		printk( KERN_INFO "=== read return : 0\n" );  // EOF 
      		return 0; 
   	} 
   	if( copy_to_user( buf, hello_str, len ) ) return -EINVAL; 
   	
	*ppos = len; 
	printk( KERN_INFO "=== read return : %d\n", len ); 
	return len; 
	} 

static ssize_t scull_release( struct inode *n, struct file *f ) { 
   device_open--; 
   return EOK; 
	}

static const struct file_operations scull_fops = {
    	.owner = THIS_MODULE, 
   	.open = scull_open, 
   	.release = scull_release, 
  	.read  = scull_read, 
}; 

static int __init scull_init( void ) {
	int ret, i; 
   	dev_t dev; 
	
   
	if( scull_major != 0 ) { 
		dev = MKDEV( scull_major, DEVICE_FIRST ); 
      		ret = register_chrdev_region( dev, DEVICE_COUNT, MODNAME ); 
   	} 
   
	else { 
	      ret = alloc_chrdev_region( &dev, DEVICE_FIRST, DEVICE_COUNT, MODNAME ); 
	      scull_major = MAJOR( dev );  // не забыть зафиксировать! 
	   } 
	if( ret < 0 ) { 
      		printk( KERN_ERR "=== Can not register char device region\n" ); 
      		goto err; 
   	} 
	
	cdev_init( &modul, &scull_fops ); 
   	modul.owner = THIS_MODULE; 
   	ret = cdev_add( &modul, dev, DEVICE_COUNT ); 
   
	if( ret < 0 ) { 
		unregister_chrdev_region( MKDEV( scull_major, DEVICE_FIRST ), DEVICE_COUNT ); 
      		printk( KERN_ERR "=== Can not add char device\n" ); 
      		goto err; 
   	} 
   
	scull_class = class_create( THIS_MODULE, "scull_class" ); 
	 
   	for( i = 0; i < DEVICE_COUNT; i++ ) { 
      		char name[ 10 ]; 
      		dev = MKDEV( scull_major, DEVICE_FIRST + i ); 
      		sprintf( name, "%s_%d", DEVNAME, i ); 
      		device_create( scull_class, NULL, dev, "%s", name ); 
   	} 
   	printk( KERN_INFO "======== module installed %d:[%d-%d] ========\n", 
        MAJOR( dev ), DEVICE_FIRST, MINOR( dev ) ); 

	err:  
   		return ret; 
	}

  
static void __exit scull_exit( void ) {
	dev_t dev; 
   	int i; 
   	for( i = 0; i < DEVICE_COUNT; i++ ) { 
      		dev = MKDEV( scull_major, DEVICE_FIRST + i ); 
      		device_destroy( scull_class, dev ); 
   		} 
   
	class_destroy( scull_class ); 
   	cdev_del( &modul ); 
   	unregister_chrdev_region( MKDEV( scull_major, DEVICE_FIRST ), DEVICE_COUNT ); 
   	printk( KERN_INFO "=============== module removed =============\n" ); 
	}

module_init(scull_init);
module_exit(scull_exit);


	
